//==--- tools/clang-check/ClangInterpreter.cpp - Clang Interpreter tool
//--------------===//
//===----------------------------------------------------------------------===//

#include "clang/AST/ASTConsumer.h"
#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Tooling.h"

using namespace clang;

#include "Environment.h"
class ReturnException : public std::exception {};
class InterpreterVisitor : public EvaluatedExprVisitor<InterpreterVisitor> {
 public:
  explicit InterpreterVisitor(const ASTContext &context, Environment *env)
      : EvaluatedExprVisitor(context), mEnv(env) {}
  virtual ~InterpreterVisitor() {}

  virtual void VisitBinaryOperator(BinaryOperator *bop) {
    // visit 孩子语句
    VisitStmt(bop);
    mEnv->binop(bop);
  }

  virtual void VisitUnaryOperator(UnaryOperator *uop) {
    VisitStmt(uop);
    this->mEnv->unaryop(uop);
  }
  virtual void VisitDeclRefExpr(DeclRefExpr *expr) {
    VisitStmt(expr);
    mEnv->declref(expr);
  }
  virtual void VisitCastExpr(CastExpr *expr) {
    VisitStmt(expr);
    mEnv->cast(expr);
  }
  virtual void VisitCallExpr(CallExpr *call) {
    // TODO 实现尾递归调用优化

    // 先递归访问不同类型的子statement，在语法树中是一些参数
    // 因此该visit也是一些参数处理
    VisitStmt(call);
    if (this->mEnv->builtinfunc(call)) {
      return;
    }

    // 创建栈帧
    this->mEnv->createstackframe(call);

    // 解释执行 函数指令
    try {
      VisitStmt(call->getDirectCallee()->getBody());
    } catch (ReturnException e) {
    }

    // 释放栈帧，返回值绑定
    // 这个设计有点问题，不一样全部访问完body才return
    this->mEnv->exitfun(call);
  }

  virtual void VisitReturnStmt(ReturnStmt *returns) {
    // 计算返回值，但不在此处进行返回操作（回收栈帧）
    // 因为有的函数可能不含有 ReturnStmt.
    // 这样可能函数就无法正常退出

    VisitStmt(returns);
    this->mEnv->returnstmt(returns);
    // 利用异常机制提前退出，跳过调用链中剩下的代码（所有函数中未被执行的代码）
    // 这个设计属实很巧妙
    throw ReturnException();
  }

  virtual void VisitDeclStmt(DeclStmt *declstmt) {
    VisitStmt(declstmt);
    mEnv->decl(declstmt);
  }

  virtual void VisitIntegerLiteral(IntegerLiteral *literal) {
    this->mEnv->literal(literal);
  }

  virtual void VisitIfStmt(IfStmt *ifstmt) {
    Expr *cond = ifstmt->getCond();

    // 此处不能用
    // VisitStmt()，因为它只会取出参数的所有子节点进行遍历而忽略当前节点本身。
    Visit(cond);

    // 根据 cond 判断的结果只去 Visit 需要执行的子树
    if (mEnv->getExprVal(cond)) {
      Visit(ifstmt->getThen());
    } else {
      if (Stmt *elseStmt = ifstmt->getElse()) {
        Visit(elseStmt);
      }
    }
  }

  virtual void VisitWhileStmt(WhileStmt *whilestmt) {
    Expr *cond = whilestmt->getCond();

    Visit(cond);
    while (mEnv->getExprVal(cond)) {
      Visit(whilestmt->getBody());
      Visit(cond);
    }
  }

  virtual void VisitForStmt(ForStmt *forstmt) {
    // init cond inc body 四部分
    // 有些部分可能为空

    if (auto init = forstmt->getInit()) {
      Visit(forstmt->getInit());
    }

    Expr *cond = forstmt->getCond();
    Visit(cond);
    while (this->mEnv->getExprVal(cond)) {
      Visit(forstmt->getBody());
      Visit(forstmt->getInc());
      Visit(cond);
    }
  }

  virtual void VisitArraySubscriptExpr(ArraySubscriptExpr *arraysubscriptexp) {
    VisitStmt(arraysubscriptexp);
    this->mEnv->arraysubscribe(arraysubscriptexp);
  }

  virtual void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *expr) {
    // sizeof
    mEnv->ueot(expr);
  }

  virtual void VisitParenExpr(ParenExpr *parenexpr) {
    VisitStmt(parenexpr);
    mEnv->paren(parenexpr);
  }

 private:
  Environment *mEnv;
};

// ASTConsumer 提供了许多不同的入口点，
// 例如 HandleTranslationUnit，它与翻译单元的 ASTContext 一起调用。
// 其中关键是 实现 RecursiveASTVisitor 以从 AST 中提取相关信息
class InterpreterConsumer : public ASTConsumer {
 public:
  explicit InterpreterConsumer(const ASTContext &context)
      : mEnv(), mVisitor(context, &mEnv) {}
  virtual ~InterpreterConsumer() {}

  virtual void HandleTranslationUnit(clang::ASTContext &Context) {
    TranslationUnitDecl *decl = Context.getTranslationUnitDecl();
    mEnv.init(decl);

    // entry为main函数的entry
    FunctionDecl *entry = mEnv.getEntry();
    // 遍历 entry->getBody() 的孩子节点
    try {
      mVisitor.VisitStmt(entry->getBody());
    } catch (ReturnException e) {
    }
  }

 private:
  Environment mEnv;
  InterpreterVisitor mVisitor;
};

class InterpreterClassAction : public ASTFrontendAction {
 public:
  // 剩下的唯一部分是实现 CreateASTConsumer 方法
  // 每个 FrontendAction 类都需要实现一个consumer
  // ASTConsumer 是一个接口，用于在 AST 上编写通用操作，而不管 AST
  // 是如何生成的。 This abstraction layer allows the client to be independent
  // of the AST producer (e.g. parser vs AST dump file reader, etc).
  virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
      clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
    return std::unique_ptr<clang::ASTConsumer>(
        new InterpreterConsumer(Compiler.getASTContext()));
  }
};

/// Usage: ./ast-interpreter "$(cat ../tests/test00.c)"
int main(int argc, char **argv) {
  if (argc > 1) {
    // 在编写基于 clang 的工具（如 Clang 插件或基于 LibTooling 的独立工具）时，
    // 常见的入口点是 FrontendAction。FrontendAction
    // 是一个接口，允许在编译过程中执行用户特定的操作 为了在 AST clang
    // 上运行工具，提供了方便的接口 ASTFrontendAction，该接口负责执行操作
    clang::tooling::runToolOnCode(
        std::unique_ptr<clang::FrontendAction>(new InterpreterClassAction),
        argv[1]);
  }
}
